#include <Windows.h>
#include <WbemIdl.h>
#include <comutil.h>
#include <string.h>

#pragma comment(lib, "wbemuuid.lib")
#pragma comment(lib, "comsuppw.lib")

#define ECRAMWRAD 0x00000000000  // EC RAM Wirte address, value must move to high bit, such as move left 16bit <<16
#define ECRAMADDR 0x10000000000  // EC RAM Read  address, with offset such as [0x0454]

#define R	0x24
#define G	0x22
#define B	0x1B

IWbemServices	*m_pSvc;

bool oops(const char *msg)
{
	fprintf(stderr, "Error: %s", msg);
	getchar();
	exit(1);
}

bool InitWMI(void)
{
	CoInitialize(0);

	LRESULT hr = CoInitializeSecurity(
		NULL,
		-1,                          // COM negotiates service  
		NULL,                        // Authentication services  
		NULL,                        // Reserved  
		RPC_C_AUTHN_LEVEL_DEFAULT,   // Default authentication   
		RPC_C_IMP_LEVEL_IMPERSONATE, // Default Impersonation    
		NULL,                        // Authentication info  
		EOAC_NONE,                   // Additional capabilities   
		NULL                         // Reserved  
	);

	if (FAILED(hr))
		oops("CoInitializeSecurity()");

	// Step 3: ---------------------------------------------------
	// Obtain the initial locator to WMI -------------------------
	IWbemLocator *pLoc = NULL;
	hr = CoCreateInstance(
		CLSID_WbemLocator,
		0,
		CLSCTX_INPROC_SERVER,
		IID_IWbemLocator, (LPVOID *)&pLoc);

	if (FAILED(hr))
		oops("CoCreateInstance()");

	// Step 4: ---------------------------------------------------
	// Connect to WMI through the IWbemLocator::ConnectServer method
	hr = pLoc->ConnectServer(
		_bstr_t(L"ROOT\\WMI"),		   // Object path of WMI namespace
		NULL,                          // User name. NULL = current user
		NULL,                          // User password. NULL = current
		0,                             // Locale. NULL indicates current
		NULL,                          // Security flags.
		0,                             // Authority (e.g. Kerberos)
		0,                             // Context object 
		&m_pSvc                        // pointer to IWbemServices proxy
	);

	if (pLoc != NULL)
	{
		pLoc->Release();
		pLoc = NULL;
	}

	if (FAILED(hr))
		oops("ConnectServer()");

	hr = CoSetProxyBlanket(
		m_pSvc,                      // Indicates the proxy to set
		RPC_C_AUTHN_WINNT,           // RPC_C_AUTHN_xxx
		RPC_C_AUTHZ_NONE,            // RPC_C_AUTHZ_xxx
		NULL,                        // Server principal name 
		RPC_C_AUTHN_LEVEL_CALL,      // RPC_C_AUTHN_LEVEL_xxx 
		RPC_C_IMP_LEVEL_IMPERSONATE, // RPC_C_IMP_LEVEL_xxx
		NULL,                        // client identity
		EOAC_NONE                    // proxy capabilities 
	);
	if (FAILED(hr))
		oops("CoSetProxyBlanket()");
	return TRUE;
}

bool WmiReadEcRam(ULONG64 ullAddr, BYTE *lpVal)
{
	// Step 6: --------------------------------------------------
	// Use the IWbemServices pointer to make requests of WMI ----

	ullAddr = ECRAMADDR + ullAddr;

	IEnumWbemClassObject *pEnum = NULL;
	LRESULT hres = m_pSvc->ExecQuery(_bstr_t(L"WQL"), //Query Language
		_bstr_t(L"Select * from AcpiTest_MULong"),    //Query to Execute
		WBEM_FLAG_RETURN_IMMEDIATELY,                 //Make a semi-synchronous call
		NULL,                                         //Context
		&pEnum                                        //Enumeration Interface
	);

	ULONG ulReturned;
	IWbemClassObject *pObj = NULL;

	//Get the Next Object from the collection 
	hres = pEnum->Next(WBEM_INFINITE, //Timeout
		1,                            //No of objects requested
		&pObj,                        //Returned Object 
		&ulReturned                   //No of object returned
	);

	if (FAILED(hres))
	{
		printf("\nWBEM_INFINITE Error: %d", GetLastError());
		return FALSE;
	}

	// Get Instance name path
	VARIANT varInstaceName;
	VariantInit(&varInstaceName);
	hres = pObj->Get(_bstr_t(L"__PATH"), 0, &varInstaceName, NULL, NULL);
	if (FAILED(hres))
	{
		VariantClear(&varInstaceName);
		printf("\n__PATH Error: %d", GetLastError());
		return FALSE;
	}

	pObj->Release();
	pObj = NULL;

	IWbemClassObject* pClass = NULL;
	hres = m_pSvc->GetObject(_bstr_t(L"AcpiTest_MULong"),/* ClassName */
		0,
		NULL,
		&pClass,
		NULL);
	if (FAILED(hres))
	{
		VariantClear(&varInstaceName);
		printf("\nAcpiTest_MULong Error: %d", GetLastError());
		return FALSE;
	}

	IWbemClassObject* pInParamsDefinition = NULL;
	hres = pClass->GetMethod(_bstr_t(L"GetSetULong"), /* MethodName */
		0,
		&pInParamsDefinition,
		NULL);
	if (FAILED(hres))
	{
		VariantClear(&varInstaceName);
		printf("\nGetSetULong Error: %d", GetLastError());
		return FALSE;
	}

	pClass->Release();
	pClass = NULL;

	IWbemClassObject* pClassInstance = NULL;
	hres = pInParamsDefinition->SpawnInstance(0, &pClassInstance);
	pInParamsDefinition->Release();
	pInParamsDefinition = NULL;

	// Create the values for the in parameters
	TCHAR szCmd[20] = { NULL };
	wsprintf(szCmd, L"%I64u", ullAddr);
	//printf("\nAddr: %0IX", ullAddr);

	VARIANT varCommand;
	VariantInit(&varCommand);
	varCommand.vt = VT_BSTR;
	varCommand.bstrVal = _bstr_t(szCmd);
	hres = pClassInstance->Put(L"Data", 0, &varCommand, CIM_UINT64);
	if (FAILED(hres))
	{
		VariantClear(&varCommand);
		VariantClear(&varInstaceName);
		printf("\nData Error: %d", GetLastError());
		return FALSE;
	}

	// Execute Method
	IWbemClassObject* pOutParams = NULL;
	hres = m_pSvc->ExecMethod(varInstaceName.bstrVal,
		_bstr_t(L"GetSetULong"), /* MethodName*/
		0,
		NULL,
		pClassInstance,
		&pOutParams,
		NULL);
	if (FAILED(hres))
	{
		VariantClear(&varCommand);
		VariantClear(&varInstaceName);
		pClassInstance->Release();
		printf("\nGetSetULong1 Error: %d", GetLastError());
		return FALSE;
	}

	// use the following code.  The return value will be in &varReturn
	VARIANT varReturnValue;
	VariantInit(&varReturnValue);
	hres = pOutParams->Get(_bstr_t(L"Return"), 0, &varReturnValue, NULL, 0);
	if (FAILED(hres))
	{
		VariantClear(&varReturnValue);
		VariantClear(&varInstaceName);
		pClassInstance->Release();
		return FALSE;
	}

	*lpVal = varReturnValue.bVal;
	//printf("--> %#02x", *lpVal);

	// Clean up
	VariantClear(&varCommand);
	VariantClear(&varInstaceName);
	VariantClear(&varReturnValue);
	pClassInstance->Release();
	pOutParams->Release();

	return TRUE;
}
